﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CarbonExchange.AmeeQuery;
using CarbonExchange.Dal;

namespace CarbonExchange.Bll
{
    public class QuestionFactory : FactoryBase<Question>
    {
        public QuestionFactory() : base() { }

        public QuestionFactory(IAmeeCommand ameeCommand) : base(ameeCommand) { }

        public QuestionFactory(Context dalContext) : base(dalContext) { }

        public QuestionFactory(IAmeeCommand ameeCommand, Context dalContext) : base(ameeCommand, dalContext) { }

        protected override void MarkItemForDeletion(Question item)
        {
            if (item.Id > 0)
            {
                item.Convert(DalContext).Deleted = true;
            }
        }

        protected override void Validate(IQueryable<Question> items, CarbonExchange.Bll.Exceptions.ValidationException exp)
        {
            items.Validate(exp, DalContext);
        }

        protected override void PopulateDataChanges(Question item, object dataItem)
        {
            var tmp = dataItem as Dal.Question;
            if (item.Id == 0)
            {
                item.Id = tmp.Id;
                item.Created = tmp.Created.ToLocalTime();
            }
            item.SectionName = tmp.Section.Name;
        }

        protected override object GetDataItemForUpdate(Question item)
        {
            return item.Convert(DalContext);
        }

        internal class JoinHelper
        {
            internal Dal.Question Question { get; set; }
            internal Dal.Section Section { get; set; }
        }

        private IQueryable<JoinHelper> GetSelection()
        {
            return DalContext.Questions.Where(x => !x.Deleted).OrderBy(x => x.QuestionName).Join(DalContext.Sections, x => x.SectionId, y => y.Id, (x, y) => new JoinHelper() { Question = x, Section = y });
        }

        public List<Question> GetAll()
        {
            return GetSelection().Convert().ToList();
        }

        public IQueryable<Question> GetAllBySectionId(int id)
        {
            return GetSelection().Where(x => x.Section.Id == id).Convert();
        }

        public Question GetById(int id)
        {
            return GetSelection().Where(x => x.Question.Id == id).SingleOrDefault().Convert();
        }

        public IQueryable<Question> GetAllUnusedBySectionId(int sectionId, int siteId)
        {
            var included = DalContext.SiteQuestions.Where(x => x.SiteId == siteId && x.Question.SectionId == sectionId && !x.Deleted).Select(x => x.QuestionId);
            return GetSelection().Where(x => x.Section.Id == sectionId && !included.Contains(x.Question.Id)).Convert();
        }
    }

    internal static class QuestionConverter
    {
        internal static Question Convert(this QuestionFactory.JoinHelper source)
        {
            return ConvertItem(source);
        }

        internal static IQueryable<Question> Convert(this IQueryable<QuestionFactory.JoinHelper> source)
        {
            return source.Select(x => ConvertItem(x));
        }

        private static Question ConvertItem(QuestionFactory.JoinHelper item)
        {
            if (item != null)
            {
                return new Question()
                {
                    Id = item.Question.Id,
                    Name = item.Question.QuestionName,
                    QuestionType = (QuestionTextConfiguration.QuestionTypes)item.Question.Type,
                    Created = DateTime.SpecifyKind(item.Question.Created, DateTimeKind.Utc).ToLocalTime(),
                    SectionId = item.Section.Id,
                    SectionName = item.Section.Name
                };
            }
            else
            {
                return null;
            }
        }

        internal static Dal.Question Convert(this Question source, Dal.Context context)
        {
            return ConvertItem(source, context);
        }

        internal static IQueryable<Dal.Question> Convert(this IQueryable<Question> source, Dal.Context context)
        {
            return source.Select(x => ConvertItem(x, context));
        }

        private static Dal.Question ConvertItem(Question item, Dal.Context context)
        {
            if (item != null)
            {
                Dal.Question dataItem = null;
                if (item.Id == 0)
                {
                    dataItem = new CarbonExchange.Dal.Question();
                    dataItem.Created = DateTime.UtcNow;
                    context.Questions.InsertOnSubmit(dataItem);
                }
                else
                {
                    dataItem = context.Questions.Where(x => x.Id == item.Id && !x.Deleted).Single();
                }
                dataItem.Type = (int)item.QuestionType;
                dataItem.QuestionName = item.Name;
                if(dataItem.SectionId != item.SectionId)
                {
                    //dataItem
                    if (dataItem.SectionLoaded)
                    {
                        dataItem.Section = context.Sections.Where(x => x.Id == item.SectionId).Single();
                    }
                    else
                    {
                        dataItem.SectionId = item.SectionId;
                    }
                }
                return dataItem;
            }
            else
            {
                return null;
            }
        }

        internal static void Validate(this IQueryable<Question> source, CarbonExchange.Bll.Exceptions.ValidationException e, Dal.Context context)
        {
            foreach (var item in source)
            {
                item.Validate(e, context);
            }
            //check duplicates within list
            var duplicates = source.GroupBy(x => x.Name).Where(x => x.Count() > 1);
            foreach (var duplicate in duplicates)
            {
                e.Add(string.Format("Name '{0}' specified more than one in update", duplicate.Key));
            }
        }

        internal static void Validate(this Question item, CarbonExchange.Bll.Exceptions.ValidationException e, Dal.Context context)
        {
            //check that name is valid and unique
            if (string.IsNullOrEmpty(item.Name))
            {
                e.Add("Name must be set");
            }
            else if(context.Questions.Where(x => x.QuestionName == item.Name && x.Id != item.Id).Count() > 0)
            {
                e.Add("Name already exists");
            }
            //check that path is valid
            if (item.QuestionType == QuestionTextConfiguration.QuestionTypes.Unknown)
            {
                e.Add("Question Type must be set");
            }
        }

    }
}
